/**
 * @file STL_TcpClientMgr.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2021-02-08
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2021
 * 
 */

#include "STL_TcpClientMgr.h"
#include <sys/epoll.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <functional>
#include <thread>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <fcntl.h>
#include <memory>
#include "STLOG.h"
#include "STL_Socket.h"

#define EPOLL_MAX_EVENTS 10 // 单次处理事件个数

namespace STL
{
    STL_TcpClientMgr::STL_TcpClientMgr() : m_epoll_fd(-1), m_run(false)
    {
        m_run = true;

        /*创建epoll_fd*/
        m_epoll_fd = STL_Socket::createEpoll();
        m_thread = std::thread(std::bind(&STL_TcpClientMgr::tcpThread, this));
    }

    STL_TcpClientMgr::~STL_TcpClientMgr()
    {
        m_run = false;
        // m_thread.join();
        if (m_epoll_fd != -1)
        {
            STL_SAFE_CLOSE(m_epoll_fd);
        }
        // m_thread.join();
        m_thread.detach(); //epoll无法触发终止，通过分离解决
    }

    int STL_TcpClientMgr::onData(int socket)
    {
        // stlog_trace("STL_TcpClientMgr::onData[%d]", socket);
        {
            std::lock_guard<std::mutex> guard(m_socketMapMutex);
            if (m_socketMap.count(socket) != 1)
            {
                stlog_warn("Invalid socket[%d]", socket);
                return -1;
            }
        }

        size_t buflen;
        long sizeRet = recv(socket, m_buffer, STL_TcpClientMgr_Buffer_Max, 0);
        int ierrno = errno;
        if (sizeRet == 0 || (sizeRet < 0 && (ierrno != EAGAIN && ierrno != EWOULDBLOCK && ierrno != EINTR)))
        {
            // perror("socket recv");
            // stlog_info("sizeRet:%ld---ierrno:%d ", sizeRet, ierrno);
            return -1;
        }
        std::shared_ptr<std::string> sp_data(new std::string((char *)m_buffer, (size_t)sizeRet));
        // stlog_trace("STL_TcpClientMgr::recvData:\n%s", sp_data->c_str());
        // stlog_trace("STL_TcpClientMgr::recvData:[%d]", socket);
        if (m_DataCallback)
            m_DataCallback(socket, sp_data);
        return 0;
    }

    void STL_TcpClientMgr::onConnect(int socket, std::string ip, uint16_t port)
    {
        if (socket != -1)
        {
            STL_Socket::addConnectionSocketToEpoll(m_epoll_fd, socket);
            {
                std::lock_guard<std::mutex> guard(m_socketMapMutex);
                if (m_socketMap.count(socket) == 1)
                {
                    stlog_warn("ERROR, Invalid socket");
                    // onDisconnect(socket); // 假设socket存在，删除已存在的socket，（正常情况此调用不会无效）
                }
                // stlog_trace("input [SOCKET: %d, IP：%s,  PORT：%d]", socket, ip.c_str(), port);
                m_socketMap[socket] = std::shared_ptr<SocketInfo>(new SocketInfo(socket, ip, port));
            }
        }
        // stlog_info("onConnect(%d,%s,%hu)", socket, ip.c_str(), port);
        if (m_ConnectCallback)
            m_ConnectCallback(socket, ip, port);
    }

    void STL_TcpClientMgr::onDisconnect(int socket)
    {
        {
            std::lock_guard<std::mutex> guard(m_socketMapMutex);
            if (m_socketMap.count(socket) == 1)
            {

                m_socketMap.erase(socket);
            }
        }
        if (m_DisconnectCallback)
            m_DisconnectCallback(socket);
    }

    int STL_TcpClientMgr::sendDataReference(int socket, std::string &data)
    {
        std::lock_guard<std::mutex> guard(m_socketMapMutex);
        if (m_socketMap.count(socket) == 1)
        {
            // stlog_trace("STL_TcpClientMgr::sendData[%d]\n%s", socket, data.c_str());
            //TODO 发送参考haicams项目，
            send(socket, data.data(), data.size(), 0);
        }
        else
        {
            stlog_warn("STL_TcpClientMgr::can't sendData[%d]\n%s", socket, data.c_str());
        }
        return 0;
    }

    int STL_TcpClientMgr::closeTcpConnect(int socket)
    {
        STL_Socket::removeConnectionSocketFromEpoll(m_epoll_fd, socket);
        onDisconnect(socket);
        STL_SAFE_CLOSE(socket);
        return 0;
    }

    int STL_TcpClientMgr::createTcpConnect(std::string ip, uint16_t port)
    {
        // STL_Socket::async_epoll_connect(ip, port, [this,ip, port](int fd_socket)
        //                                 { this->onConnect(fd_socket, ip, port); });

        // STL_Socket::async_select_connect(ip, port, [this, ip, port](int fd_socket)
        //                                  { this->onConnect(fd_socket, ip, port); });

        // STL_Socket::async_connect(ip, port, [this, ip, port](int fd_socket)
        //                           { this->onConnect(fd_socket, ip, port); });

        STL_Socket::sync_connect(ip, port, [this, ip, port](int fd_socket)
                                 { this->onConnect(fd_socket, ip, port); });
    }

    void STL_TcpClientMgr::tcpThread()
    {
        do
        {
            auto list = STL_Socket::getEpollEvent(m_epoll_fd);
            for (int i = 0; i < list.size(); i++)
            {
                int conn_sock = list.at(i);
                int ret = onData(conn_sock);
                if (ret < 0)
                {
                    closeTcpConnect(conn_sock);
                }
            }
        } while (m_run);
    }

} // namespace STL